<h2>
<img src="../images/111-bytecode.gif" height="91" width="111" alt="Create and Read J2SE 5.0 Annotations with the ASM Bytecode Toolkit" border="0" align="left" hspace="10" vspace="0">
Create and Read J2SE 5.0 Annotations with the ASM Bytecode Toolkit</h2>
by Eugene Kuleshov<br />
10/20/2004<br clear="all" />

<p>The <a href="tutorial.html">previous article</a> in this series showed how the <a href="http://asm.objectweb.org/">ASM toolkit</a> can be used to generate new code, and modify existing classes by adding or changing code. This is suitable for many cases, but there are times
when it is necessary to stick some meta-information into the class bytecode and then access
it later on. A typical example of such metadata is the annotation facility 
introduced in J2SE 5.0.
</p>

<p style="margin: 0px; margin-bottom: 0px; margin-left: 0px; 
margin-right: 0px; margin-top: 0px; padding: 0px; padding-bottom: 0px; 
padding-left: 0px; padding-right: 0px; padding-top: 0px; font-size: 
1px;"></p>

<h3>Bytecode Attributes</h3>

<p>Annotations are actually stored in bytecode with several special attributes. The 
binary format for these and all other standard attributes is described in the 
<a href="http://java.sun.com/docs/books/vmspec/">Java Virtual Machine (JVM) specification</a> 
(which has been <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/jvms-proposed-changes.html">updated for JDK 1.5</a>). Here is a short outline of attributes supported by ASM's
<code>org.objectweb.asm.attrs</code> package.</p>

<ul>
<li><p><code>EnclosingMethod</code><br />
Used for anonymous or local classes.</p></li>

<li><p><code>LocalVariableTypeTable</code><br />
Used by debuggers to determine the value of a given local variable during the execution of a method.</p></li>
</ul>

<p>The following attributes have been introduced in the J2SE 5.0 VM.</p>

<ul>
<li><p><code>Signature</code><br />
Introduced in <a href="http://www.jcp.org/en/jsr/detail?id=14">JSR-14</a> 
  ("Adding Generics to the Java Programming Language") and used for classes, fields, and methods
  to carry generic type information in a backwards-compatible way.</p></li>

<li><p><code>SourceDebugExtension</code><br />
 Defined in <a href="http://www.jcp.org/en/jsr/detail?id=45">JSR-45</a> 
  ("Debugging Support for Other Languages") and used for classes only. 
  This attribute allows debuggers keep a reference to the original non-Java source.</p></li>
  
<li><p><code>RuntimeInvisibleAnnotations</code>, <code>RuntimeInvisibleParameterAnnotations</code>,  <code>RuntimeVisibleAnnotations</code>, <code>RuntimeVisibleParameterAnnotations</code>,   <code>AnnotationDefault</code><br />
Used to store annotations as defined in
  <a href="http://www.jcp.org/en/jsr/detail?id=175">JSR-175</a> ("A Metadata Facility for the Java Programming Language").</p></li>
</ul>

<p>There is also an attribute that hasn't been included in the J2SE 5.0 release but may be added 
in the future. For now this attribute is used for J2ME MIDlets and is generated by
the CLDC preverifier tool.</p>

<ul>
<li><p><code>StackMap</code><br />
 Contains information for the two-step bytecode verifier used by CDLC;
  its definition is given in the appendix "CLDC Byte Code Typechecker Specification" 
  in the CLDC 1.1 specification.</p></li>
</ul>

<p>All other nonstandard attributes will be ignored by Sun's JVM, although vendors may use proprietary
attributes to implement additional features without breaking bytecode compatibility.
For example, Microsoft's JVM  used the attributes <code>ActualAccessFlags</code>,
<code>Hidden</code>, <code>LinkUnsafe</code>, <code>NAT_L</code>, and <code>NAT_L_DCTS</code>
to enable bindings to native libraries without using JNI.  These attributes were
generated by the MS <code>jvc</code> java compiler, based on instructions in its JavaDoc. For example, the code
below will create the attribute <code>NAT_L</code>:</p>

<pre><code>/**
 * @dll.import("SHELL32",auto)
 */
private native static boolean 
    ShellExecuteEx(SHELLEXECUTEINFO pshex);</code></pre>

<p>Nonstandard attributes could be also used by some application containers to persist proprietary
metadata in the bytecode. However, the metadata attributes introduced in Java 5, as mentioned above, 
eliminate the need for such custom attributes.</p>

<h3>Java 5 Annotations Support in ASM</h3>

<p>ASM provides a generic API for bytecode attributes.
All attributes supported by ASM extend the <code>Attribute</code> class
and override the <code>read()</code> and <code>write()</code> 
methods in order to load and save attribute structures. Concrete attribute classes 
used to represent annotations are shown in Figure 1.
All of them use the <code>Annotation</code> data object to store
the actual values for annotations.
</p>

<p><img src="../images/asm2-attributes3.gif" width="430" height="500" alt="Figure 1" /><br />
<i>Figure 1. UML class diagram for ASM annotation attributes</i></p>

<p><code>RuntimeVisibleAnnotations</code> and <code>RuntimeInvisibleAnnotations</code>
contain <code>List</code>s of <code>Annotation</code>s.
<code>RuntimeVisibleParameterAnnotations</code> and <code>RuntimeInvisibleParameterAnnotations</code>
contain <code>List</code>s of <code>List</code>s of <code>Annotation</code>s in order to handle
multiple annotations for each method parameter.</p>

<p>When parsing existing bytecode, <code>ClassReader</code> builds 
concrete attributes from the bytecode data and sends them as 
parameters to the appropriate <code>visit...</code> methods of <code>ClassVisitor</code> and 
<code>CodeVisitor</code>. The same events can be generated manually,
as we will see later.</p>
    
<ul>
<li>The <code>ClassVisitor.visitAttribute()</code> method receives events for class-level
  attributes, and it is where annotations for class will be passed.</li>
<li>The <code>ClassVisitor.visitField()</code> method receives events for fields, so all
  field annotations are passed as an <code>attrs</code> parameter of this method.</li>
<li>The <code>ClassVisitor.visitMethod()</code> method receives events for every method,
  so both method and method parameter annotations are passed as an <code>attrs</code>
  parameter of this method.</li>
</ul>

<p>Notice that the <code>ClassVisitor.visitAttribute()</code> and <code>CodeVisitor.visitAttribute()</code> 
methods are called for every attribute. Attributes in <code>ClassVisitor.visitField()</code> 
and <code>ClassVisitor.visitMethod()</code> are represented as a linked list.</p>

<p>The Java Virtual Machine specification defines structures and restrictions
for all attributes, and I recommend keeping the "Class File Format" chapter handy.
However, the <code>ASMifier</code> utility can help to implement required
transformations with minimal knowledge of bytecode.
Let's pick a simple class and apply a custom <code>Marker</code> 
annotation to see how it will be handled by the ASM API.  Here's
a trivial <code>Calculator1</code> class:</p>

<pre><code>public class Calculator1 {
  private int result;

  private void sum( int i1, int i2) {
    result = i1 + i2;
  }
}</code></pre>

<p>Here is the definition of the <code>Marker</code> annotation.</p>

<pre><code>@Retention(RetentionPolicy.RUNTIME)
public @interface Marker {
  String value();
}</code></pre>

<p>And this is an annotated version of <code>Calculator</code> class, called <code>Calculator2</code>.</p>

<pre><code>@Marker("Class")
public class Calculator2 {
  @Marker("Field")
  private int result;

  @Marker("Method")
  private void sum( int i1, @Marker("Parameter") int i2) {
    result = i1 + i2;
  }
}</code></pre>

<p>Now we can compile <code>Calculator1</code> and <code>Calculator2</code>
and run <code>ASMifierClassVisitor</code> on  both compiled classes,
and then compare the results to see the ASM API calls required to add annotation 
attributes into bytecode.
The comparison result is below.
Red lines represent code without annotations and green lines represent
code that has been added to generate annotation attributes 
in bytecode for the <code>Calculator2</code> class.</p>

<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td><pre><code>  ...
  ClassWriter cw = new ClassWriter(false);
  CodeVisitor cv;

  cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER,</code></pre></td>
</tr>
<tr>
<td bgcolor="#ffc0c0"><pre><code>-     "<b>Calculator1</b>", "java/lang/Object", null, 
-     "<b>Calculator1.java</b>");</code></pre></td>
</tr>
<tr>
<td bgcolor="#d0ffd0"><pre><code>+     "<b>Calculator2</b>", "java/lang/Object", null, 
+     "<b>Calculator2.java</b>");</code></pre></td>
<td><pre><code></code></pre></td>
</tr>
<tr>
<td bgcolor="#d0ffd0"><pre><code>+ // FIELD ATTRIBUTES
+ RuntimeInvisibleAnnotations <b>fAtt1</b> = 
+   	new RuntimeInvisibleAnnotations();
+ Annotation fAtt1ann0 = new Annotation("LMarker;");
+ fAtt1ann0.add( "value", "Field");
+ fAtt1.annotations.add( fAtt1ann0);</code></pre></td>
</tr>
<tr>
<td><pre><code>  cw.visitField(ACC_PRIVATE, "result", "I", null,</code></pre></td>
</tr>
<tr>
<td bgcolor="#ffc0c0"><pre><code>-     <b>null</b>);</code></pre></td>
</tr>
<tr>
<td bgcolor="#d0ffd0"><pre><code>+     <b>fAtt1</b>);</code></pre></td>
</tr>
<tr>
<td><pre><code>
  {
  cv = cw.visitMethod(ACC_PUBLIC, "&lt;init&gt;", "()V", 
      null, null);
  cv.visitVarInsn(ALOAD, 0);
  cv.visitMethodInsn(INVOKESPECIAL, 
      "java/lang/Object", "&lt;init&gt;", "()V");
  cv.visitInsn(RETURN);
  cv.visitMaxs(1, 1);
  }
  {</code></pre></td>
</tr>
<tr>
<td bgcolor="#d0ffd0"><pre><code>+ // METHOD ATTRIBUTES
+ RuntimeInvisibleParameterAnnotations <b>mAtt1</b> = 
+   	new RuntimeInvisibleParameterAnnotations();
+ List mAtt1p0 = new ArrayList();
+ mAtt1.parameters.add( mAtt1p0);
+
+ List mAtt1p1 = new ArrayList();
+ Annotation mAtt1p1a0 = new Annotation("LMarker;");
+ mAtt1p1a0.add( "value", "Parameter");
+ mAtt1p1.add( mAtt1p1a0);
+ mAtt1.parameters.add( mAtt1p1);
+
+ RuntimeInvisibleAnnotations mAtt2 = 
+   	new RuntimeInvisibleAnnotations();
+ Annotation mAtt2a0 = new Annotation("LMarker;");
+ mAtt2a0.add( "value", "Method");
+ mAtt2.annotations.add( mAtt2a0);
+
+ mAtt1.next = mAtt2;
+</code></pre></td>
</tr>
<tr>
<td><pre><code>  cv = cw.visitMethod(ACC_PRIVATE, "sum", "(II)V",</code></pre></td>
</tr>
<tr>
<td bgcolor="#ffc0c0"><pre><code>-     null, <b>null</b>);</code></pre></td>
</tr>
<tr>
<td bgcolor="#d0ffd0"><pre><code>+     null, <b>mAtt1</b>);</code></pre></td>
</tr>
<tr>
<td><pre><code>  cv.visitVarInsn(ALOAD, 0);
  cv.visitVarInsn(ILOAD, 1);
  cv.visitVarInsn(ILOAD, 2);
  cv.visitInsn(IADD);</code></pre></td>
</tr>
<tr>
<td bgcolor="#ffc0c0"><pre><code>- cv.visitFieldInsn(PUTFIELD, "<b>Calculator1</b>",</code></pre></td>
</tr>
<tr>
<td bgcolor="#d0ffd0"><pre><code>+ cv.visitFieldInsn(PUTFIELD, "<b>Calculator2</b>",</code></pre></td>
</tr>
<tr>
<td><pre><code>      "result", "I");</code></pre></td>
</tr>
<tr>
<td><pre><code>  cv.visitInsn(RETURN);
  cv.visitMaxs(3, 3);
  }</code></pre></td>
</tr>
<tr>
<td bgcolor="#d0ffd0"><pre><code>+ {
+ // CLASS ATRIBUTE
+ <b>RuntimeInvisibleAnnotations attr = 
+     new RuntimeInvisibleAnnotations();
+ Annotation attrann0 = new Annotation("LMarker;");
+ attrann0.add( "value", "Class");
+ attr.annotations.add( attrann0);
+ cw.visitAttribute(attr);</b>
+ }</code></pre></td>
</tr>
<tr>
<td><pre><code>  cw.visitEnd();
  ...</code></pre></td>
</tr>
</table>

<p>It's common practice to generate or transform classes at runtime using a custom 
<code>ClassLoader</code>. We can also use this technique to add Java 5 annotations.
A <code>ClassLoader</code> implementation may use the following code to do the required 
transformation on loaded classes.</p>

<pre><code>ClassWriter cw = new ClassWriter(false);
try {
    ClassReader cr = 
      new ClassReader(url.openStream());
    cr.accept(new MarkerClassVisitor(cw), 
      Attributes.getDefaultAttributes(), false);
    
    byte[] b = cw.toByteArray();
    return defineClass( name, b, 0, b.length);
} catch( Exception ex) {
    throw new ClassNotFoundException(
        "Unable to load class "+name);
}</code></pre>

<p>The actual transformation is done by <code>MarkerClassVisitor</code>.
It changes the bytecode version in the <code>visit()</code> method and 
adds a class-level <code>Marker</code> annotation using the code 
from the above comparison, before delegating the call to the 
<code>visitEnd()</code> method of the chained <code>ClassVisitor</code>.</p>

<pre><code>public static class MarkerClassVisitor 
    extends ClassAdapter {

  public MarkerClassVisitor(ClassVisitor cv) {
    super(cv);
  }
  
  public void visit( int version, int access, 
      String name, String superName, 
      String[] interfaces, String sourceFile) {
    super.visit(<b>Constants.V1_5</b>, access, name, 
        superName, interfaces, sourceFile);
  }
  
  public void visitEnd() {
    <b>String t = Type.getDescriptor(Marker.class);
    Annotation ann = new Annotation(t);
    ann.add("value", "Class");

    RuntimeVisibleAnnotations attr = 
      new RuntimeVisibleAnnotations();
    attr.annotations.add(ann);      
    cv.visitAttribute(attr);</b>
    
    super.visitEnd();
  }

}</code></pre>

<p>Below is a simple JUnit test case that uses the Java 5 reflection API to 
verify that the <code>Marker</code> annotation has been created. You can find the 
complete source code in the <a href="/pub/a/onjava/2004/10/20/asm.html?page=2#resources">Resources</a> section below.</p>

<pre><code>public class MarkerClassLoaderTest extends TestCase {

  public void testLoadClass() throws Exception {
    MarkerClassLoader cl = 
        new MarkerClassLoader(getClass());
    Class c = cl.loadClass( "asm.Calculator1");
    Annotation a = c.getAnnotation(Marker.class);
    assertNotNull( "Expecting Marker", a);
  }

}</code></pre>

<h3>Reading J2SE 5.0 Annotations</h3>

<p>As shown above, annotations can be generated and accessed from Java 5 code; however,
it would be interesting to access these annotations from older JVMs. Let's see 
how an adapter class, similar to the Java 5 reflection API, could use the ASM toolkit 
to access this information. </p>

<p>Here is the public part of the <code>AnnotatedClass</code> adapter.</p>

<pre><code>public class AnnotatedClass {
  private AnnReader r;

  public AnnotatedClass(Class c) {
    try {
      URL u = c.getResource("/"+
          c.getName().replace('.', '/')+".class");
      r = new AnnReader(u.openStream());
    } catch(IOException ex) {
      throw new RuntimeException(ex.toString());
    }
  }
  
  public AnnotatedClass(InputStream is) {
    try {
      r = new AnnotationReader(is);
    } catch(IOException ex) {
      throw new RuntimeException(ex.toString());
    }
  }

  public Ann[] getAnnotations() {
    List anns = r.getClassAnnotations();
    return (Ann[]) anns.toArray(new Ann[0]);
  }

  ... 
}</code></pre>

<p>The method <code>getAnnotations()</code> substitutes for a 
new method with the same name in the Java 5 API. However, because
<code>java.lang.annotation.Annotation</code> class can't be used, our method 
return the marker interface <code>Ann</code>.</p>

<pre><code>public static interface Ann {
}</code></pre>

<p>Client code that uses the above class would cast the received
<code>Ann</code> instance into the corresponding interface.</p>

<pre><code>Class c =  Calculator2.class;
AnnotatedClass ac = new AnnotatedClass(c);
Ann[] anns = ac.getAnnotations();
if( anns[0] instanceof Marker) {
    String value = ((Marker)anns[0]).value();
    ...
}</code></pre>

<p>The tricky part is that the <code>Marker</code> annotation
class can't be used directly with older JREs, because its bytecode
version is only accepted by Java 5 VM and it contains a few additional
flags not recognized by the older JVMs. However,
it is easy to transform it on the fly and make it a
plain Java interface by comparing the results produced by the
<code>ASMifierClassVisitor</code> utility or just manually creating
and compiling such an interface to be used with old JREs.
</p>

<p>Annotation data is loaded by the <code>AnnReader</code> class, which 
extends ASM's <code>ClassAdapter</code> and redefines the
<code>visitAttribute()</code>, <code>visitField()</code>, and
<code>visitMethod()</code> methods.</p>

<pre><code>public class AnnReader 
      extends ClassAdapter {
  private List classAnns = new ArrayList();
  private Map fieldAnns = new HashMap();
  private Map methodAnns = new HashMap();
  private Map methodParamAnns = new HashMap();


  public AnnReader(InputStream is) 
      throws IOException {
    super(null);
    ClassReader r = new ClassReader(is);
    r.accept(this, 
        Attributes.getDefaultAttributes(), true);
  }

  public void visitAttribute(Attribute attr) {
    classAnns.addAll(loadAnns(attr));
  }
  
  public void visitField(int access, 
        String name, String desc, Object value, 
        Attribute attrs) {
    fieldAnns.put(name+desc, loadAnns(attrs));
  }
  
  public CodeVisitor visitMethod(int access, 
        String name, String desc, 
        String[] exceptions, Attribute attrs) {
    methodAnns.put(name+desc, loadAnns(attrs));
    methodParamAnns.put(name+desc, 
        loadParamAnns(attrs));
    return null;
  }

  ... </code></pre>

<p>The <code>loadAnns()</code> and <code>loadParamAnns()</code> methods
are very straightforward. They just iterate through annotations
and collect all values into a <code>List</code>, using the <code>loadAnn()</code>
method. Each element in the <code>List</code> would be a dynamic proxy that implements the
<code>Ann</code> interface and the interface declared by the
annotation (e.g., <code>Marker</code>).</p>

<pre><code>  private List loadAnns(Attribute a) {
    List anns = new ArrayList();
    while(a!=null) {
      if(a instanceof 
          RuntimeVisibleAnnotations) {
        RuntimeVisibleAnnotations ra = 
            (RuntimeVisibleAnnotations) a;
        addAnns(anns, ra.annotations);
      } else if(a instanceof 
          RuntimeInvisibleAnnotations) {
        ...
      }
      a = a.next;
    }
    return anns;
  }

  private List loadParamAnns(Attribute a) {
    List anns = new ArrayList();
    while(a!=null) {
      if(a instanceof 
          RuntimeVisibleParameterAnnotations) {
        RuntimeVisibleParameterAnnotations ra = 
          (RuntimeVisibleParameterAnnotations) a;
        addParamAnns( anns, ra.parameters);
      } else if(a instanceof 
          RuntimeInvisibleParameterAnnotations) {
        ...
      }
      a = a.next;
    }
    return anns;
  }

  private void addParamAnns( List anns, List params) {
    for(Iterator it = params.iterator(); it.hasNext();) {
      List paramAttrs = (List) it.next();
      List paramAnns = new ArrayList();
      addAnns(paramAnns, paramAttrs);
      anns.add(paramAnns);
    }
  }

  private void addAnns(List anns, List attr) {
    for(int i = 0; i&lt;attr.size(); i++) {
      anns.add(<b>loadAnn</b>((Annotation) attr.get(i)));
    }
  }</code></pre>

<p>Method <code>loadAnn()</code> is responsible for creating a dynamic proxy
from the values retrieved from an <code>Annotation</code> object.
The proxy is created using <code>AnnInvocationHandler</code>, which tries
to find a value in the map with the same key as the method name.
It is also creates a summary in case <code>toString()</code> is called,
and throws a <code>RuntimeException</code> otherwise.</p>

<pre><code>  private Object loadAnn(Annotation annotation) {
    String type = annotation.type;
    List vals = annotation.elementValues;
    List nvals = new ArrayList(vals.size());
    for(int i = 0; i &lt; vals.size(); i++) {
      Object[] element = (Object[]) vals.get(i);
      String name = (String) element[0];
      Object value = <b>getValue</b>(element[1]);
      nvals.add(new Object[] { name, value});
    }
    
    try {
      Type t = Type.getType(type);
      String cname = t.getClassName();
      Class typeClass = Class.forName(cname);
      ClassLoader cl = getClass().getClassLoader();
      return <b>Proxy.newProxyInstance(cl, 
          new Class[] { Ann.class, typeClass}, 
          new AnnInvocationHandler(type, nvals));</b>
    
    } catch(ClassNotFoundException ex) {
      throw new RuntimeException(ex.toString());
    
    }
  }</code></pre>

<p>Finally, the <code>getValue()</code> method recursively converts
annotation values into Java types.
It  also wraps nested annotations into dynamic proxies
using the <code>loadAnn()</code> method.</p>

<pre><code>  private Object getValue(Object value) {
    if (value instanceof EnumConstValue) {
      // TODO convert to java.lang.Enum adapter
      return value;
    }
    if (value instanceof Type) {
      String cname = ((Type)value).getClassName();
      try {
        return Class.forName(cname);
      } catch(ClassNotFoundException e) {
        throw new RuntimeException(e.toString());
      }
    }
    if (value instanceof Annotation) {
      return loadAnn(((Annotation) value));
    }
    if (value instanceof Object[]) {
      Object[] values = (Object[]) value;
      Object[] o = new Object[ values.length];
      for(int i = 0; i &lt; values.length; i++) {
        o[ i] = getValue(values[ i]);
      }
      return o;
    }

    return value;
  }</code></pre>

<p>In fact, the above code allows you to read annotation data that is not
available through the Java 5 reflection API. For example, you can retrieve annotations
with <code>RetentionPolicy.CLASS</code>.</p>

<h3>Conclusion</h3>

<p>J2SE 5's annotation facility opens new possibilities for
declarative component configuration. Some scenarios
may require the dynamic manipulation of annotations in the
runtime, and this is provided by the ASM toolkit, which offers complete 
support for bytecode attributes used to persist Java 5 annotation data. 
It also allows access to those attributes from older JREs and can 
even read non-visible annotations at runtime.</p>

<h3><a name="resources">Resources</a></h3>

<ul>
<li><a href="asm2.zip">Source code</a> for this article</li>
<li><a href="http://asm.objectweb.org/">ASM project page</a></li>
<li><a href="http://java.sun.com/docs/books/vmspec/">Java Virtual Machine Specification</a></li>
<li>"<a href="http://java.sun.com/docs/books/vmspec/2nd-edition/ClassFileFormat-final-draft.pdf">Revised Class File Format</a>" (Chapter 4 of the JVM specification; PDF). Includes modifications for J2SE 5.0 to support changes mandated by JSR-14, JSR-175, and JSR-201, as well as minor corrections and adjustments.</li>
</ul>
